﻿/*
* Copyright (c) 2007-2009 SlimDX Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.Diagnostics;
using SampleFramework.Utilities;


namespace SlimDxControlExtention
{
    /// <summary>
    /// Presents an easy to use wrapper for making games and samples.
    /// </summary>
    public abstract class GraghicsApp : IDisposable
    {
        GraghicsAppTime gameTime = new GraghicsAppTime();
        TimeSpan maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
        TimeSpan totalGameTime;
        TimeSpan accumulatedElapsedGameTime;
        TimeSpan lastFrameElapsedGameTime;
        TimeSpan lastFrameElapsedRealTime;
        TimeSpan targetElapsedTime = TimeSpan.FromTicks(166667);
        TimeSpan inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
        int updatesSinceRunningSlowly1 = int.MaxValue;
        int updatesSinceRunningSlowly2 = int.MaxValue;
        bool forceElapsedTimeToZero;
        bool drawRunningSlowly;
        long lastUpdateFrame;
        float lastUpdateTime;

        /// <summary>
        /// Occurs when the game is disposed.
        /// </summary>
        public event EventHandler Disposed;

        /// <summary>
        /// Occurs when the game is activated.
        /// </summary>
        public event EventHandler Activated;

        /// <summary>
        /// Occurs when the game is deactivated.
        /// </summary>
        public event EventHandler Deactivated;

        /// <summary>
        /// Occurs when the game is exiting.
        /// </summary>
        public event EventHandler Exiting;

        /// <summary>
        /// Occurs when a drawing frame is about to start.
        /// </summary>
        public event CancelEventHandler FrameStart;

        /// <summary>
        /// Occurs when a drawing frame ends.
        /// </summary>
        public event EventHandler FrameEnd;

        /// <summary>
        /// Gets the resources collection.
        /// </summary>
        /// <value>The resources collection.</value>
        public ResourceCollection Resources
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the interpolators collection.
        /// </summary>
        /// <value>The interpolators collection.</value>
        public Collection<Interpolator> Interpolators
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the triggers collection.
        /// </summary>
        /// <value>The triggers collection.</value>
        public Collection<Trigger> Triggers
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the components collection.
        /// </summary>
        /// <value>The components collection.</value>
        public GameComponentCollection Components
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the game window.
        /// </summary>
        /// <value>The game window.</value>
        public GraghicsAppControl Control
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the graphics device manager.
        /// </summary>
        /// <value>The graphics device manager.</value>
        public GraphicsDeviceManager GraphicsDeviceManager
        {
            get;
            private set;
        }

        /// <summary>
        /// Initializes the <see cref="Game"/> class.
        /// </summary>
        static GraghicsApp()
        {
            // configure SlimDX
            Configuration.ThrowOnError = true;
            Configuration.AddResultWatch(ResultCode.DeviceLost, ResultWatchFlags.AlwaysIgnore);
            Configuration.AddResultWatch(ResultCode.WasStillDrawing, ResultWatchFlags.AlwaysIgnore);

#if DEBUG
            Configuration.DetectDoubleDispose = true;
            Configuration.EnableObjectTracking = true;
#else
            Configuration.DetectDoubleDispose = false;
            Configuration.EnableObjectTracking = false;
#endif

            // setup the application
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Game"/> class.
        /// </summary>
        protected GraghicsApp()
        {

            Resources = new ResourceCollection();
            Components = new GameComponentCollection();
            Interpolators = new Collection<Interpolator>();
            Triggers = new Collection<Trigger>();

            Control = new GraghicsAppControl();
            Control.Paint += Window_Paint;

            GraphicsDeviceManager = new GraphicsDeviceManager(this);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // GraphicsDeviceManager.Dispose will come around and call the Dispose(bool)
            // overload, so we don't need to do it here. It's convoluted, but it works well.
            if (GraphicsDeviceManager != null)
                GraphicsDeviceManager.Dispose();
            GraphicsDeviceManager = null;

            if (Disposed != null)
                Disposed(this, EventArgs.Empty);

            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// Allows the game to perform logic processing.
        /// </summary>
        /// <param name="gameTime">The time passed since the last update.</param>
        protected virtual void Update(GraghicsAppTime gameTime)
        {
            // process each trigger
            for (int i = Triggers.Count - 1; i >= 0; i--)
            {
                // check if the trigger has a predicate that is allowing us to proceed
                Trigger trigger = Triggers[i];
                if (trigger.ActivationPredicate != null && trigger.ActivationPredicate())
                {
                    trigger.Activate();
                    if (!trigger.Repeat)
                        Triggers.RemoveAt(i);
                }
                else
                {
                    // set the start time if it doesn't yet have one
                    if (trigger.StartTime == 0)
                        trigger.StartTime = gameTime.TotalGameTime;
                    else if (gameTime.TotalGameTime - trigger.StartTime > trigger.Duration)
                    {
                        trigger.Activate();
                        if (!trigger.Repeat)
                            Triggers.RemoveAt(i);
                        else
                            trigger.StartTime = gameTime.TotalGameTime;
                    }
                }
            }

            // process each interpolator
            for (int i = Interpolators.Count - 1; i >= 0; i--)
            {
                // make sure it isn't dead
                Interpolator interpolator = Interpolators[i];
                if (interpolator.IsFinished)
                    Interpolators.RemoveAt(i);
                else
                    interpolator.Update();
            }

            // update the components
            foreach (IGameComponent component in Components)
                component.Update(gameTime);
        }

        /// <summary>
        /// Called when a frame is ready to be drawn.
        /// </summary>
        /// <param name="gameTime">The time passed since the last frame.</param>
        public virtual void CommitDraw()
        {
            // draw the components
            foreach (IGameComponent component in Components)
                component.Draw();
        }

        public virtual void SetMultiViewerConf(int width, int height, int horMons, int vertMons,int row,int col)
        {
            fullWallHeight = height;
            fullWallWidth = width;
            verticalMonitorCount = vertMons;
            horizontalMonitorCount = horMons;
            rowInGrid = row;
            colInGrid = col;
        }

        protected Matrix preProjection = new Matrix();
        protected int horizontalMonitorCount, verticalMonitorCount;
        protected int fullWallWidth, fullWallHeight,rowInGrid,colInGrid;
        private SampleFramework.Utilities.PreProjectionSplitter splitter = new SampleFramework.Utilities.PreProjectionSplitter();

        protected virtual void UpdatePreprojectionMatrix(Camera cam)
        {
            splitter.LoadCamera(cam, horizontalMonitorCount, verticalMonitorCount);
            preProjection = splitter.GetPreProjection(rowInGrid, colInGrid);
        }

        public virtual void PrepareDraw(GraghicsAppTime time)
        {
            
        }

        /// <summary>
        /// Initializes the game.
        /// </summary>
        protected internal virtual void Initialize()
        {
            // update the resources
            Resources.Initialize(GraphicsDeviceManager);
        }

        /// <summary>
        /// Loads graphical resources.
        /// </summary>
        protected internal virtual void LoadContent()
        {
            // update the resources
            Resources.LoadContent();
        }

        /// <summary>
        /// Unloads graphical resources.
        /// </summary>
        protected internal virtual void UnloadContent()
        {
            // update the resources
            Resources.UnloadContent();
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected internal virtual void Dispose(bool disposing)
        {
            // update the resources
            Resources.Dispose();
        }

        /// <summary>
        /// Raises the <see cref="E:Activated"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnActivated(EventArgs e)
        {
            if (Activated != null)
                Activated(this, e);
        }

        /// <summary>
        /// Raises the <see cref="E:Deactivated"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnDeactivated(EventArgs e)
        {
            if (Deactivated != null)
                Deactivated(this, e);
        }

        /// <summary>
        /// Raises the <see cref="E:Exiting"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnExiting(EventArgs e)
        {
            if (Exiting != null)
                Exiting(this, e);
        }

        /// <summary>
        /// Raises the <see cref="E:FrameStart"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        protected virtual void OnFrameStart(CancelEventArgs e)
        {
            if (FrameStart != null)
                FrameStart(this, e);
        }

        /// <summary>
        /// Raises the <see cref="E:FrameEnd"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnFrameEnd(EventArgs e)
        {
            if (FrameEnd != null)
                FrameEnd(this, e);
        }

        /*public void DrawFrame(GraghicsAppTime time)
        {
            try
            {
                if (!Control.IsMinimized)
                {
                    CancelEventArgs e = new CancelEventArgs(false);
                    OnFrameStart(e);
                    if (!e.Cancel)
                    {
                        gameTime = time;
                        Draw(gameTime);

                        OnFrameEnd(EventArgs.Empty);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Exception in drawFrame: "+ex.Message);
            }
            finally
            {
                lastFrameElapsedGameTime = TimeSpan.Zero;
                lastFrameElapsedRealTime = TimeSpan.Zero;
            }
        }*/

        void Window_Paint(object sender, PaintEventArgs e)
        {
            //DrawFrame(gameTime);
        }
    }
}
